/*************************************************************************
  >      File Name: main_pub.c
  >    Description:   This file

  >         Author: Yang Xiaohan
  >           Mail: yangxiaohan_niki@163.com
  >   Created Time: Mon 18 Jul 2022 13:35:17 CST
  >   Changed Time: Mon 18 Jul 2022 13:35:17 CST
 ************************************************************************/
#include <stdio.h>
#include <getopt.h>
#include <unistd.h>
#include <libgen.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "ds18b20.h"
#include "get_data.h"
#include "database.h"
#include "sqlite3.h"
#include "iniparser.h"
#include "dictionary.h"
#include "cJSON.h"
#include "mosquitto.h"
#include "configini.h"

#define PATH  "../etc/tencentyun.ini"

void connect_callback(struct mosquitto *mosq,void *obj,int rc);
void disconnect_callback(struct mosquitto *mosq,void *obj,int rc);
void publish_callback(struct mosquitto *mosq,void *obj,int rc);
int	cJSON_package(struct mosquitto *mosq,void *obj,float temp,char *msg);
int run_flag = 0;

void sig_handler(int signum)
{
	if(signum == SIGUSR1)
	{
		run_flag = 1;
	}
}

void printf_usage(char *progname)
{
	printf("%s usage:\n",progname);
	printf("-d(--daemon:set program running on background.\n");
	printf("-h(--help:print this help information.\n");
	printf("Example: %s -h\n",progname);

	return;
}

int main (int argc, char **argv)
{
	int						ret;
	char					*progname = basename(argv[0]);
	int						opt = -1;
	int						daemon_run = 0;
	int						log_fd;
	time_t					now_time;
	time_t					last_time = 0;
	double					wait_time = 30;
	struct mosquitto		*mosq;
	struct mqtt_user_data   mqtt;

	struct option		options[] =
	{
		{"daemon",no_argument,NULL,'d'},
		{"help",no_argument,NULL,'h'},
		{NULL, 0, NULL, 0}
	};

	while((opt = getopt_long(argc,argv,"dh",options,NULL)) != -1)
	{
		switch(opt)
		{
			case 'd':
				daemon_run = 1;
				break;
			case 'h':
				printf_usage(argv[0]);
				return -1;
			default:
				break;
		}
	}

	if(daemon_run)
	{
		printf("program %s running in the background\n",progname);
		if( (log_fd = open("../tmp/tencent.log",O_CREAT|O_RDWR, 0666)) < 0)
		{
			printf("open() log file failure:%s\n",strerror(errno));
			return -1;
		}
		dup2(log_fd, STDOUT_FILENO);//标准输出重定向到tencent.log文件中
		dup2(log_fd, STDERR_FILENO);//标准出错重定向到tencent.log文件中

		daemon(1,1);
	}
	signal(SIGUSR1,sig_handler);

	//载入解析文件
	memset(&mqtt, 0, sizeof(mqtt));
	ret = get_config(PATH, &mqtt, PUB);
	if(ret != 0)
	{
		printf("get_config() failure\n");
		return -1;
	}

	//mosquitto初始化
	mosquitto_lib_init();

	//创建mosquitto实例
	mosq = mosquitto_new(mqtt.client_id, true, (void *)&mqtt);
	if(mosq == NULL)
	{
		printf("Create a new client failure!\n");
		goto cleanup;
	}

	//设置客户端用户名和密码
	ret = mosquitto_username_pw_set(mosq,mqtt.username,mqtt.password);
	if(ret != MOSQ_ERR_SUCCESS)
	{
		printf("set username and password failure!\n");
		goto cleanup;
	}

	//设置回调函数
	mosquitto_connect_callback_set(mosq,connect_callback);
	mosquitto_disconnect_callback_set(mosq,disconnect_callback);
	mosquitto_publish_callback_set(mosq,publish_callback);


	while(!run_flag)
	{
		time(&now_time);
		if(now_time - last_time >= wait_time)
		{
			//连接服务器
			ret = mosquitto_connect(mosq,mqtt.brokeraddress,mqtt.brokerport,mqtt.KEEP_ALIVE);
			if(ret != MOSQ_ERR_SUCCESS)
			{
				printf("mosquitto_connect failure:%s\n",strerror(errno));
				goto cleanup;
			}

			
			//网络循环，调用回调函数
			ret = mosquitto_loop_forever(mosq,10000,1);
			if(ret != MOSQ_ERR_SUCCESS)
			{
				printf("mosquitto_loop_forever() failure:%s\n",strerror(errno));
				goto cleanup;
			}
			printf("Connection is broken.\n");
			last_time = now_time;
		}
	}

cleanup:
	close(log_fd);
	mosquitto_destroy(mosq);
	mosquitto_lib_cleanup();
	return 0;
} 

void connect_callback(struct mosquitto *mosq, void *obj,int rc)
{
	float					temp = 0.0;
	char					time_buf[64];
	char					sample_buf[1024];
	char					dev_id[32];
	char					msg_buf[512];
	int						ret = -1;
	int						sample_flag = 0;
	struct mqtt_user_data   *mqtt;
	sqlite3					*db = NULL;

	printf("Callback on function: on_connect.\n");

	memset(sample_buf,0,sizeof(sample_buf));
	memset(time_buf,0,sizeof(time_buf));
	memset(dev_id,0,sizeof(dev_id));

	//获取采样时间、设备信息、温度值
	ret = get_all_data(dev_id,time_buf,&temp,sample_buf);
	if(0 != ret)
	{
		printf("Get temperature sample failure:%s.\n",strerror(errno));
		return;
	}
	printf("Get temperature sample: %s\n",sample_buf);
	sample_flag = 1;

	mqtt = (mqtt_user_data *)obj;
	
	//温度值以JSON格式封装上报
	memset(msg_buf,0,sizeof(msg_buf));
	ret = cJSON_package(mosq,obj,temp,msg_buf);
	if(ret != 0)
	{
		printf("Packaging data failure:%s\n",strerror(errno));
		return;
	}
	
	//判断是否连接成功，连接失败则重新连接，若连接成功则发送消息
	if(rc != 0)
	{
		printf("Connect refuse!!\n");
		mosquitto_disconnect(mosq);
	}
	else
	{
		printf("Connect with server[%s:%d] successfully!\n",mqtt->brokeraddress,mqtt->brokerport);
		if(sample_flag)
		{
			//打开数据库
			ret = db_open(&db);
			if(ret != 0)
			{
				printf("Open database and Create table failure.\n");
				return;
			}
			printf("Open database and Create table successfully!\n");

			//发送数据
			ret = mosquitto_publish(mosq,NULL,mqtt->topic,strlen(msg_buf)+1,msg_buf,mqtt->QOS,0);
			if(ret != MOSQ_ERR_SUCCESS)
			{
				printf("publish msg to server failure:%s,and save in database.\n",strerror(errno));
				
				//数据发送失败，放入数据库
				ret = db_insert(db,sample_buf);
				if(ret != 0)
				{
					printf("Save sample in database failure:%s\n",strerror(errno));
					return;
				}
				printf("Save sample in database successfully!\n");

				db_close(db);
				return;
			}
			printf("publish msg to server successfully!\n");
			printf("The message is:\n%s\n",msg_buf);
			sample_flag = 0;

			//发送成功，存入数据库
			ret = db_insert(db,sample_buf);
			if(ret != 0)
			{
				printf("Insert data to database failure.\n");
				return;
			}
			printf("Insert data to database successfully!\n");
			
			//关闭数据库
			ret = db_close(db);
			if(ret != 0)
			{
				printf("Close database failure.\n");
				return;
			}
			printf("Close database successfully!\n");
		}
	}

	//断开连接，返回loop()
	mosquitto_disconnect(mosq);
}
void disconnect_callback(struct mosquitto *mosq,void *obj,int rc)
{
	printf("Callback on function: on_disconnect finish.\n");
}
void publish_callback(struct mosquitto *mosq,void *obj,int rc)
{
	printf("Callback on function: on_publish finish.\n");
}

int cJSON_package(struct mosquitto *mosq,void *obj,float temp,char *msg_buf)
{
	cJSON						*root = NULL;
	cJSON						*item1 = NULL;
	cJSON						*item2 = NULL;
	char						*str = NULL;
	char						temper[10];
	double						c_temp = 0;
	struct mqtt_user_data		*mqtt = NULL;

	mqtt = (mqtt_user_data *)obj;

	memset(temper,0,sizeof(temper));
	snprintf(temper,10,"%0.2f",temp);
	c_temp = atof(temper);

	root = cJSON_CreateObject();
	item1 = cJSON_CreateObject();
	item2 = cJSON_CreateObject();

	
	if(strcmp(mqtt->platform,"tencentyun") == 0)
	{

		cJSON_AddItemToObject(root, "type", cJSON_CreateString(mqtt->type));
		cJSON_AddItemToObject(root, "state", item1);
		cJSON_AddItemToObject(item1, "reported", item2);
		cJSON_AddItemToObject(item2, "temperature", cJSON_CreateNumber(c_temp));
		cJSON_AddItemToObject(root, "clientToken",cJSON_CreateString(mqtt->clientToken));
	}
	else if(strcmp(mqtt->platform,"aliyun") == 0)
	{
		cJSON_AddItemToObject(root,"method",cJSON_CreateString(mqtt->method));
		cJSON_AddItemToObject(root,"id",cJSON_CreateString(mqtt->id));
		cJSON_AddItemToObject(root,"params",item1);
		cJSON_AddItemToObject(item1,mqtt->identifier,cJSON_CreateNumber(c_temp));
		cJSON_AddItemToObject(root, "version", cJSON_CreateString(mqtt->version));
	}
	str = cJSON_Print(root);
	snprintf(msg_buf,512,"%s",str);

	return 0;
}
